﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using Loki.Utils;

namespace Loki.Data.SqlServer
{
    internal class SqlDatabaseCommand : LokiObject, IDatabaseCommand
    {
        private const string PREFIX = "@";

        public virtual void AddParameter(IDataParameter P_Parameter)
        {
            _Parameters.Add((SqlParameter)P_Parameter);
        }

        public void AddDateParameter(string P_ValueName, DateTime P_Value)
        {
            if (P_Value.Year > 1752)
            {
                AddParameter(DatabaseUtils.PutValue<DateTime>(P_ValueName, P_Value, (x) => new SqlParameter(PREFIX + P_ValueName, null)));
            }
            else
            {
                AddParameter(DatabaseUtils.PutValue<DateTime>(P_ValueName, P_Value, (x) => new SqlParameter(PREFIX + P_ValueName, SqlDbType.DateTime2)));
            }
        }

        public void AddParameter<T>(string P_ValueName, T P_Value)
        {
            if (P_Value is DateTime)
            {
                AddDateParameter(P_ValueName, Convert.ToDateTime(P_Value, CultureInfo.InvariantCulture));
            }
            else if (typeof(T) == typeof(DateTime?) && P_Value != null)
            {
                AddDateParameter(P_ValueName, Convert.ToDateTime(P_Value, CultureInfo.InvariantCulture));
            }
            else
            {
                AddParameter(DatabaseUtils.PutValue<T>(P_ValueName, P_Value, (x) => new SqlParameter(PREFIX + P_ValueName, default(T))));
            }
        }

        public void AddParameter<T>(string P_ValueName, T P_Value, Func<T> P_DefaultValue)
        {
            AddParameter(DatabaseUtils.PutValue<T>(P_ValueName, P_Value, (x) => new SqlParameter(PREFIX + P_ValueName, default(T)), P_DefaultValue));
        }

        public SqlDatabaseCommand(string P_Name)
        {
            Name = P_Name;
            _Parameters = new List<SqlParameter>();
            Type = CommandType.StoredProcedure;
        }

        public SqlDatabaseCommand(string P_Name, CommandType P_Type)
            : this(P_Name)
        {
            Type = P_Type;
        }

        public string Dump()
        {
            if (Type == CommandType.Text)
            {
                string L_TextCopy = this.Name;
                foreach (IDataParameter L_Param in Parameters)
                {
                    L_TextCopy = L_TextCopy.Replace(L_Param.ParameterName, L_Param.Value.ToString());
                }

                return L_TextCopy;
            }
            else
            {
                var L_ValuedParameters = _Parameters.Select(x => string.Format(CultureInfo.InvariantCulture, DUMP_PARAMETER_VALUE_FORMAT, x.ParameterName, x.Value));

                return string.Format(CultureInfo.InvariantCulture, DUMP_FORMAT, Name, string.Join(DUMP_SEPARATOR, L_ValuedParameters));
            }
        }

        private const string DUMP_FORMAT = "EXEC {0}({1})";

        private const string DUMP_PARAMETER_VALUE_FORMAT = "{0} = {1}";

        private const string DUMP_SEPARATOR = ",";

        public string Name
        {
            get;
            private set;
        }

        public CommandType Type
        {
            get;
            private set;
        }

        private List<SqlParameter> _Parameters;

        public IEnumerable<IDataParameter> Parameters
        {
            get
            {
                return _Parameters;
            }
        }
    }
}
